package util.essential.io;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.util.AbstractList;
import java.util.Vector;

import util.essential.Configuration;

public class FileConfig {
	// Constants:

	// Attributes:
	private Vector<Configuration> configurations;/*
												 * Contains every loaded
												 * configurations.
												 */

	public static FileConfig instance;

	// Constructors: With Singleton pattern.
	private FileConfig() {
		configurations = new Vector<Configuration>();
	}

	public static FileConfig getInstance() {
		if (instance == null) {
			instance = new FileConfig();
		}
		return instance;
	}

	// Methods:
	public boolean reloadConfigurationFile(String fileName) {
		// Verifying whether exists:
		for (int i = 0; i < this.configurations.size(); i++) {
			if (((Configuration) this.configurations.elementAt(i))
					.getFileName().equals(fileName)) {
				this.configurations.removeElementAt(i);
				return loadConfigurationFile(fileName);
			}
		}
		return false;
	}

	public boolean loadConfigurationFile(String fileName) {
		// Verifying whether not exists:
		if (!this.existsConfiguration(fileName)) {
			// Load values from the given file name:
			Vector<String> tmp = getSource(fileName);
			if (tmp != null) {
				this.configurations.addElement(new Configuration(
						fileName, tmp));
				return true;
			} else
				return false;
		} else
			return true;
	}

	public boolean existsConfiguration(String fileName) {
		// Get the one SourceGetter instance:
		FileConfig tmp_instance = FileConfig.getInstance();
		// Verifying whether exists:
		for (int i = 0; i < tmp_instance.configurations.size(); i++)
			if (((Configuration) tmp_instance.configurations.elementAt(i))
					.getFileName().equals(fileName))
				return true;
		return false;
	}

	/**
	 * @param file_name
	 * @return a vector with every variable names and its values.
	 */
	public Vector<String> getSource(String fileName) {
		Vector<String> tmp = new Vector<String>();
		try {

			File file = new File(fileName);
			BufferedReader bufferedReader = new BufferedReader(
					new InputStreamReader(new FileInputStream(file)));
			while (bufferedReader.ready())
				try {
					tmp.addElement(bufferedReader.readLine());
				} catch (OptionalDataException e) {
					e.printStackTrace();
					return null;
				}
			bufferedReader.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		return tmp;
	}

	/**
	 * @param fileName
	 * @return object recovered from a file.
	 */
	public Object loadObjectFromAFile(String fileName) {
		Object tmp = null;
		try {
			File file = new File(fileName);
			if (file.exists()) {
				FileInputStream in = new FileInputStream(file);
				ObjectInputStream input = new ObjectInputStream(in);
				tmp = input.readObject();
				in.close();
				input.close();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return tmp;
	}

	/**
	 * @param fileName
	 * @return whether was possible to store object.
	 */
	public boolean storeObjectToAFile(String fileName, Object object) {
		try {
			File file = new File(fileName);
			if (file.exists())
				return false;
			else {
				file.createNewFile();
				FileOutputStream out = new FileOutputStream(file);
				ObjectOutputStream output = new ObjectOutputStream(out);
				output.writeObject(object);
				output.close();
				output.close();
				return true;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * @param fileName
	 * @return whether was possible to store object.
	 */
	public boolean replaceObjectInAFile(String fileName, Object object) {
		try {
			File file = new File(fileName);
			if (file.exists())
				file.delete();
			file.createNewFile();
			FileOutputStream out = new FileOutputStream(file);
			ObjectOutputStream output = new ObjectOutputStream(out);
			output.writeObject(object);
			output.close();
			output.close();
			return true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * @param fileName
	 * @return whether was possible to store plain text.
	 */
	public boolean replaceTextFile(String fileName, String text) {
		try {
			File file = new File(fileName);
			if (file.exists())
				file.delete();
			file.createNewFile();
			FileOutputStream out = new FileOutputStream(file);
			DataOutputStream output = new DataOutputStream(out);
			output.write(text.getBytes());
			output.close();
			output.close();
			return true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * @param variable
	 * @param configuration_file
	 * @return the value of a variable in its string representation.
	 */
	public String getVariableValue(String fileName, String variable) {
		// Loads configuration file weather it doesn't exist:
		this.loadConfigurationFile(fileName);
		// Search the variable value expected:
		for (int i = 0; i < this.configurations.size(); i++)
			if (((Configuration) this.configurations.elementAt(i))
					.getFileName().equals(fileName))
				return ((Configuration) this.configurations
						.elementAt(i)).getVariableValue(variable);
		return null;
	}

	public String[][] getConfigurationSet(String fileName) {
		// Loads configuration file weather it doesn't exist:
		this.loadConfigurationFile(fileName);
		// Search the variable value expected:
		for (int i = 0; i < this.configurations.size(); i++)
			if (((Configuration) this.configurations.elementAt(i))
					.getFileName().equals(fileName))
				return ((Configuration) this.configurations
						.elementAt(i)).getVariablesAndValues();
		return null;
	}

	public String getStringList(String fileName) {
		// Loads configuration file weather it doesn't exist:
		this.loadConfigurationFile(fileName);
		// Search the variable value expected:
		for (int i = 0; i < this.configurations.size(); i++)
			if (((Configuration) this.configurations.elementAt(i))
					.getFileName().equals(fileName))
				return ((Configuration) this.configurations
						.elementAt(i)).getStringList();
		return null;
	}

	public String getStringRepresentation(AbstractList<String> list) {
		return this.getStringRepresentation("","", list);
	}

	public String getStringRepresentation(String tab,
			AbstractList<String> list) {
		return this.getStringRepresentation(tab, "", list);
	}
	public String getStringRepresentation(String tab,
			String delimiter,
			AbstractList<String> list){

		StringBuffer buff = new StringBuffer();
		final String EMPTY_STRING = "";
		int counter = 0;
		for (String string : list) {
			buff.append(tab + string + (((counter+1)<list.size())?delimiter:EMPTY_STRING));
			counter++;
		}
		return new String(buff.toString());
	}
}
